package cc.risingsun.wing.core;

import static cc.risingsun.wing.core.generator.WingGeneratorUtil.getTypeGenerator;
import static cc.risingsun.wing.core.generator.WingGeneratorUtil.registerTypeAnnotations;
import static cc.risingsun.wing.core.reflect.ReflectionHelper.getAnnotations;

import java.lang.annotation.Annotation;
import java.util.Map;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import cc.risingsun.wing.core.scanner.ScannerSkipException;
import cc.risingsun.wing.core.scanner.ScannerSkipException.SkipCode;

/**
 * 
 * @author iron.yin@gmail.com
 */
public abstract class AnnotatedClassVisitor extends ClassVisitor implements
		Opcodes {

	private String name;
	private String superName;
	private String[] interfaces;
	// 根据不同的annotation需要的值是不同的,例如,SQLAccessInterface就只可以被标注在接口上,不能标注类
	private int access;
	protected volatile boolean isAnnotated;
	private final Map<String, Class<? extends Annotation>> annotations;
	protected WingClass clazz;

	protected AnnotatedClassVisitor(Class<? extends Annotation>... annotations) {
		this(null, annotations);
	}

	protected AnnotatedClassVisitor(ClassVisitor cv, Class<? extends Annotation>... annotations) {
		super(ASM4, cv);
		this.annotations = getAnnotations(annotations);
		registerTypeAnnotations(annotations);
	}

	@Override
	public final void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
		if (!((access & ACC_PUBLIC) != 0)) {
			throw new ScannerSkipException(SkipCode.NONPUBLIC, this.getClass().getSimpleName(), name);
		}
		this.name = name;
		this.superName = superName;
		this.interfaces = interfaces;
		this.access = access;
		super.visit(version, access, name, signature, superName, interfaces);
	}

	@Override
	public final AnnotationVisitor visitAnnotation(String desc, boolean visible) {
		if (annotations != null) {
			Class<? extends Annotation> annoClass = annotations.get(desc);
			if (annoClass != null) {
				// 检查该类型的access,根据不同的annotaion校验条件不同
				getTypeGenerator(annoClass).checkType(access, annoClass.getSimpleName(), ASMUtil.reverse(name));
				isAnnotated = true;
				if (clazz == null)
					clazz = new WingClass(access, superName, name, interfaces);
				final WingAnnotation annotation = clazz.createAnnotation(name, desc, annotations.get(desc));
				return new AnnotationVisitorHolder(annotation, super.visitAnnotation(desc, visible));
			}
		}
		return super.visitAnnotation(desc, visible);
	}

	@Override
	public final FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
		if (isAnnotated) {
			final WingField field = clazz.createField(name, desc);
			final AnnotatedFieldVisitor afv = getAnnotatedFieldVisitor(super.visitField(access, name, desc, signature, value));
			afv.setASMField(field);
			return afv;
		}
		return super.visitField(access, name, desc, signature, value);
	}

	@Override
	public final MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
		if (isAnnotated) {
			final WingMethod method = clazz.createMethod(name, desc, exceptions);
			final AnnotatedMethodVisitor amv = getAnnotatedMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
			amv.setASMMethod(method);
			return amv;
		}
		return super.visitMethod(access, name, desc, signature, exceptions);
	}

	@Override
	public void visitEnd() {
		// 将处理好的ASM类添加到factory中,在factory中进行处理
		if (isAnnotated)
			WingFactory.addClass(clazz);
		super.visitEnd();
		isAnnotated = false;
	}

	protected abstract AnnotatedFieldVisitor getAnnotatedFieldVisitor(FieldVisitor superVisitor);

	protected abstract AnnotatedMethodVisitor getAnnotatedMethodVisitor(MethodVisitor superVisitor);
}
